/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: app_manager.cpp
 *
 * Purpose: application manager implementation.
 *
 * Developer:
 *   wen.gu , 2020-09-15
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#define LOG_TAG "APPM"
#include "app_manager.h"

#include <map>

#include "panda/com/client_requester.h"
#include "panda/core/utils.h"

#include "panda/core/log.h"

namespace panda
{

/******************************************************************************
 **    MACROS
 ******************************************************************************/

#define APP_MANAGER "app_manager"

#define DO_FOCUS_CHANGE "doFocusChange"
#define APP_PHONE_PRIORITY 1
/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/

using panda::com::ServiceRequestHandlePtr;
using panda::com::ClientRequester;
/******************************************************************************
 **    inner FUNCTION DEFINITIONS
 ******************************************************************************/
static AppRequestFocusType Str2RequestFocusType(const std::string& type)
{
    if (type == "Exclusive")
    {
        return AppRequestFocusType::Exclusive;
    }
    else if (type == "Coexistence")
    {
        return AppRequestFocusType::Coexistence;
    }

    return AppRequestFocusType::Unknown;
}

static AppFocusState RequestFocusType2State(AppRequestFocusType type)
{
    if (type == AppRequestFocusType::Coexistence)
    {
        return AppFocusState::CoexistenceOff;
    }
    else if (AppRequestFocusType::Exclusive == type)
    {
        return AppFocusState::Off;
    }

    return AppFocusState::Unknown;
}

std::string FocusState2Str(AppFocusState state)
{
    if (AppFocusState::On == state)
    {
        return "focus_on";
    }
    else if (AppFocusState::Off == state)
    {
        return "focus_off";
    }
    else if (AppFocusState::CoexistenceOff == state)
    {
        return "coexistence_focus_off";
    }

    return "";
}
/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/

AppManager::AppManager()
{
    /** todo, something */
}

AppManager::~AppManager()
{
    /** todo, something */
    if (mProvider)
    {
        delete mProvider;
    }
}

AppManager::PandaErrc AppManager::initialize()
{

    mProvider = new ServiceProvider(APP_MANAGER);

    mProvider->registerMethod("requestFoucs", [this](ServiceRequestHandlePtr srhdl, const std::string& method, const Value& param)
    {
            PandaErrc ret = onRequestFocus(param);
            Value reply;
            panda::core::Error2Json(ret, reply);
            mProvider->sendResponse(srhdl, reply);
    });


    mProvider->registerMethod("releaseFoucs", [this](ServiceRequestHandlePtr srhdl, const std::string& method, const Value& param)
    {
        onReleaseFocus(param);
        Value reply;
        mProvider->sendResponse(srhdl, reply);
    });

    PandaErrc ret = mProvider->start();  /** todo, process, callbacks */

    return ret;

}


AppManager::PandaErrc AppManager::onRequestFocus(const Value& param)
{
    if (param.isMember("name") && param.isMember("type"))
    {

        uint8_t priority = 0;

        if (param.isMember("priority"))
        {
            priority = param["priority"].asUInt();
        }


        std::string appName = param["name"].asString();
        AppRequestFocusType reqType = Str2RequestFocusType(param["type"].asString());
        bool isDesktop = false;
        if (param.isMember("is_desktop"))
        {
            isDesktop = param["is_desktop"].asBool();
        }

        AppStateStack::iterator it = mStateStack.begin();

        if (it != mStateStack.end())  /** check the top of stack is phone App */
        {
            stackNode& sn = *it;

            if (sn.priority == APP_PHONE_PRIORITY)
            {
                return PandaErrc::Fail;
            }
        }

        findAndRemoveFromStack(appName);

        tellOtherAppFocusLost(RequestFocusType2State(reqType));

        tellAppGainFocus(appName);

        stackNode node;
        node.appName = appName;
        node.priority = priority;
        node.state = AppFocusState::On;

        mStateStack.push_front(node);  /** stack push */
        return PandaErrc::OK;

    }

    return PandaErrc::BadParameter;
}

void AppManager::onReleaseFocus(const Value& param)
{
    if (param.isMember("name"))
    {
        std::string appName = param["name"].asString();
        AppStateStack::iterator it = mStateStack.begin();
        bool checkHead = true;

        while (it != mStateStack.end())
        {  
            stackNode& sn = *it;
            if (sn.appName == appName)
            {   
                if (checkHead) /** here will release focus at the top of stack */
                {   
                    tellAppLostFocus(appName); /** todo, refine me */
                    mStateStack.erase(it); /** todo, refine me?? */
                    it = mStateStack.begin();

                    if (it != mStateStack.end()) /** have other app in stack, so tell the app gained focus */
                    {
                        tellAppGainFocus((*it).appName);
                    }
                }
                else
                { /** not the top, then just remove current node.*/
                    
                    mStateStack.erase(it); /** todo, refine me?? */
                }

                break;
            }

            ++it;
            checkHead = false;
        }
    }
}

void AppManager::findAndRemoveFromStack(const std::string& appName) /** find current app is already exist in stack */
{
    AppStateStack::iterator it = mStateStack.begin();
    while (it != mStateStack.end()) /** find target is exist in stack, if exist, then remove it. */
    {
        stackNode& sn = *it;
        if (sn.appName == appName) /**exist in stack, so remove it */
        {
            mStateStack.erase(it);
            break;
        }
        ++it;
    }
}

void AppManager::tellOtherAppFocusLost(AppFocusState lostState)
{
    AppStateStack::iterator it = mStateStack.begin();
    Value param;
    Value reply;
    param["type"] = FocusState2Str(lostState);
    while (it != mStateStack.end()) /** notify other app lost focus. */
    {
        stackNode& sn = *it;
        param["name"] = sn.appName;
        ClientRequester::methodCall(sn.appName, DO_FOCUS_CHANGE, param, reply); /** todo, refine me??? */
        sn.state = lostState;
        ++it;
    }
}

void AppManager::tellAppGainFocus(const std::string& appName)
{
    Value param;
    Value reply;
    param["type"] = FocusState2Str(AppFocusState::On);   
    param["name"] = appName;
    ClientRequester::methodCall(appName, DO_FOCUS_CHANGE, param, reply); /** todo, refine me??? */
}

void AppManager::tellAppLostFocus(const std::string& appName)
{
    Value param;
    Value reply;
    param["type"] = FocusState2Str(AppFocusState::Off);
    param["name"] = appName;
    ClientRequester::methodCall(appName, DO_FOCUS_CHANGE, param, reply); /** todo, refine me??? */
}


} /** namespace panda */


int main(int argc, char* argv[])
{
    panda::AppManager appm;
    panda::AppManager::PandaErrc ret = appm.initialize();
    if (ret == panda::AppManager::PandaErrc::OK)
    {
        while (1)
        {
            panda::core::SleepMs(1000);
        }
    }
    else
    {
        LOGE("app manager initialize failed(%s)\n", panda::core::ErrorStr(ret));
    }

    return 0;
}
